package com.zby.ss.core;

import com.sun.javafx.runtime.async.BackgroundExecutor;
import com.zby.ss.core.builder.FactoryBuilder;
import com.zby.ss.core.constants.AutowireType;
import com.zby.ss.core.util.*;
import com.zby.ss.core.vo.XmlBeanProperty;
import com.zby.ss.core.vo.XmlConfigBean;
import com.zby.ss.util.ArrayUtil;
import com.zby.ss.util.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;

/**
 * Created By zby on 11:17 2019/2/14
 * 类的上下文加载顺序
 */

public class ClassPathXmlApplicationContext {

    private static Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class.getName());

    private String configXml;

    public ClassPathXmlApplicationContext(String configXml) {
        this.configXml = configXml;
    }

    /**
     * Created By zby on 18:38 2019/2/24
     * bean对应的id的名称
     */
    public Object getBean(String name) {
        String dirPath="../simulaspring/src/main/resources/";
        Map<String, Map<String, XmlConfigBean>> allXmls = LoadConfig.loadXmlConFig(dirPath).getAllXmls();
        boolean contaninsKey = MapUtil.findKey(allXmls, configXml);
        if (!contaninsKey) {
            throw new RuntimeException("配置文件不存在" + configXml);
        }
        Map<String, XmlConfigBean> beans = allXmls.get(configXml);
        contaninsKey = MapUtil.findKey(beans, name);
        if (!contaninsKey) {
            throw new RuntimeException("id为" + name + "bean不存在");
        }
        XmlConfigBean configFile = beans.get(name);
        if (null == configFile) {
            throw new RuntimeException("id为" + name + "bean不存在");
        }
        String classPath = configFile.getClazz();
        if (StringUtils.isBlank(classPath)) {
            throw new RuntimeException("id为" + name + "类型不存在");
        }
        String autowire = configFile.getAutowire();
        if (StringUtils.isBlank(autowire)) {
            return getBeanWithoutArgs(beans, classPath, configFile);
        } else {
            switch (autowire) {
                case AutowireType.BY_NAME:
                    return getBeanByName(beans, classPath, configFile);
                case AutowireType.CONSTRUCTOR:
                    return getBeanByConstruct(classPath, configFile);
                case AutowireType.AUTODETECT:
                    return getByAutodetect(beans, classPath, configFile);
                case AutowireType.BY_TYPE:
                    return getByType(beans, classPath, configFile);
            }
        }

        return null;
    }

    /**
     * Created By zby on 18:33 2019/2/24
     * 在没有设置自动装配时，通过ref对象
     */
    private Object getBeanWithoutArgs(Map<String, XmlConfigBean> beans, String classPath, XmlConfigBean configFile) {
        //属性名称
        String proName = null;
        try {
            Class currentClass = Class.forName(classPath);
            //通过引用 ref 创建对象
            Set<XmlBeanProperty> properties = configFile.getProperties();
            //如果没有属性，就返回，便于下面的递归操作
            if (CollectionUtil.isEmpty(properties)) {
                return currentClass.newInstance();
            }
            Class<?> superClass = currentClass.getSuperclass();
            //TODO 父类的集合
//            List<Class> superClasses = null;
            //在创建子类构造器之前，创建父类构造器，
            // 父类构造器的参数子类构造器的参数
            Object currentObj = null;
            //当前构造器
            Object consArgsObj = null;
            String consArgsName = null;
            boolean hasSuperClass = (null != superClass && !superClass.getSimpleName().equals("Object"));
            if (hasSuperClass) {
                Constructor[] constructors = currentClass.getDeclaredConstructors();
                ArrayUtil.validateArray(superClass, constructors);
                Parameter[] parameters = constructors[0].getParameters();
                if (parameters == null || parameters.length == 0) {
                    consArgsObj = constructors[0].newInstance();
                } else {
                    ArrayUtil.validateArray(superClass, parameters);
                    consArgsName = parameters[0].getType().getSimpleName();
                    //配置文件大类型,与参数构造器的类型是否相同
                    for (XmlBeanProperty property : properties) {
                        String ref = property.getRef();
                        if (StringUtils.isNotBlank(ref) && ref.equalsIgnoreCase(consArgsName)) {
                            classPath = beans.get(ref).getClazz();
                            Class<?> clazz = Class.forName(classPath);
                            consArgsObj = clazz.newInstance();
                        }
                    }
                    currentObj = constructors[0].newInstance(consArgsObj);
                }
            } else {
                currentObj = currentClass.newInstance();
            }
            for (XmlBeanProperty property : properties) {
                //这里适合用递归，无限调用自身
                //通过name找到属性，配置文件中是否有该属性，通过ref找到其对应的bean文件
                proName = property.getName();
                Field field = currentClass.getDeclaredField(proName);
                if (null != field) {
                    String ref = property.getRef();
                    Object value = property.getValue();
                    //如果没有赋初值，就通过类型创建
                    if (null == value && StringUtils.isNotBlank(ref)) {
                        boolean flag = StringUtils.isNotBlank(consArgsName) && null != consArgsObj && consArgsName.equalsIgnoreCase(ref);
                        //递归调用获取属性对象
                        value = flag ? consArgsObj : getBean(ref);
                    }
                    field.setAccessible(true);
                    Transfomer.transformerPropertyValue(currentObj, field, value);
                }
            }
            return currentObj;
        } catch (ClassNotFoundException e) {
            logger.error("名为" + classPath + "类不存在");
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            logger.error(classPath + "类的属性" + proName + "不存在");
            throw new RuntimeException(classPath + "类的属性" + proName + "不存在");
        }
        return null;
    }

    /**
     * Created By zby on 21:16 2019/3/1
     * 根据属性名事项自动装配，
     */
    private Object getBeanByName(Map<String, XmlConfigBean> beans, String classPath, XmlConfigBean configFile) {
        String proName = null;
        try {
            Class currentClass = Class.forName(classPath);
            Class superclass = currentClass.getSuperclass();
            Method[] methods = currentClass.getDeclaredMethods();
            List<Method> methodList = MethodHelper.filterSetMethods(methods);
            Object currentObj = currentClass.newInstance();
            Set<XmlBeanProperty> properties = configFile.getProperties();
            //配置文件中，但是有父类，
            if (CollectionUtil.isEmpty(properties)) {
                boolean isExit = null != superclass && !superclass.getSimpleName().equals("Object");
                if (isExit) {
                    Field[] parentFields = superclass.getDeclaredFields();
                    if (ArrayUtil.isNotEmpty(parentFields)) {
                        if (CollectionUtil.isNotEmpty(methodList)) {
                            for (Field parentField : parentFields) {
                                for (Method method : methodList) {
                                    if (MethodHelper.methodNameToProName(method.getName()).equals(parentField.getName())) {
                                        //如果有泛型的话
                                        Type genericType = currentClass.getGenericSuperclass();
                                        if (null != genericType) {
                                            String genericName = genericType.getTypeName();
                                            genericName = StringUtils.substring(genericName, genericName.indexOf("<") + 1, genericName.indexOf(">"));
                                            Class genericClass = Class.forName(genericName);
                                            method.setAccessible(true);
                                            method.invoke(currentObj, genericClass);
                                        }
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                    }

                }
                return currentObj;
            }
            //传递给父级对象 service -- 》value
            List<Method> tmpList = new ArrayList<>();
            Map<String, Object> map = new HashMap<>();
            Object value = null;
            for (XmlBeanProperty property : properties) {
                proName = property.getName();
                if (ArrayUtil.isNotEmpty(methods)) {
                    String ref = property.getRef();
                    value = property.getValue();
                    for (Method method : methodList) {
                        String methodName = MethodHelper.methodNameToProName(method.getName());
                        Field field = currentClass.getDeclaredField(methodName);
                        if (methodName.equals(proName) && null != field) {
                            if (null == value && StringUtils.isNotBlank(ref)) {
                                value = getBean(ref);
                            } else if (value != null && StringUtils.isBlank(ref)) {
                                value = Transfomer.transformerPropertyValue(field.getType().getSimpleName(), value);
                            }
                            method.setAccessible(true);
                            method.invoke(currentObj, value);
                            map.put(proName, value);
                            tmpList.add(method);
                            break;
                        }
                    }
                }
            }
            tmpList = MethodHelper.removeMethod(methodList, tmpList);
            for (Method method : tmpList) {
                Class<?>[] type = method.getParameterTypes();
                if (ArrayUtil.isEmpty(type)) {
                    throw new RuntimeException("传递给父级对象的参数为空type=" + type);
                }
                for (Class<?> aClass : type) {
                    String superName = ClassHelper.classNameToProName(aClass.getSimpleName());
                    value = map.get(superName);
                    method.setAccessible(true);
                    method.invoke(currentObj, value);
                }
            }
            return currentObj;
        } catch (ClassNotFoundException e) {
            logger.error("名为" + classPath + "类不存在");
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            logger.error("类" + classPath + "属性" + proName + "不存在");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Created By zby on 23:06 2019/3/2
     */
    private Object getBeanByConstruct(String classPath, XmlConfigBean configFile) {
        try {
            Class currentClass = Class.forName(classPath);
            Set<XmlBeanProperty> properties = configFile.getProperties();
            if (CollectionUtil.isEmpty(properties)) {
                return currentClass.newInstance();
            }
            ///构造器参数类型和构造器对象集合
            Object[] objects = new Object[properties.size()];
            Class<?>[] paramType = new Class[properties.size()];
            Field[] fields = currentClass.getDeclaredFields();
            int i = 0;
            for (Iterator iterator = properties.iterator(); iterator.hasNext(); i++) {
                XmlBeanProperty property = (XmlBeanProperty) iterator.next();
                String proName = property.getName();
                String ref = property.getRef();
                Object value = property.getValue();
                for (Field field : fields) {
                    Class<?> type = field.getType();
                    String typeName = type.getSimpleName();
                    String paramName = field.getName();
                    if (paramName.equals(proName) && ObjectUtil.isNotNull(value) && StringUtils.isBlank(ref)) {
                        objects[i] = Transfomer.transformerPropertyValue(typeName, value);
                        paramType[i] = type;
                        break;
                    } else if (paramName.equals(proName) && StringUtils.isNotBlank(ref) && ObjectUtil.isNull(value)) {
                        objects[i] = getBean(ref);
                        paramType[i] = type;
                        break;
                    }
                }
            }
            return currentClass.getConstructor(paramType).newInstance(objects);
        } catch (ClassNotFoundException e) {
            logger.error("名为" + classPath + "类不存在");
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Created By zby on 2:27 2019/3/3
     * 如果一个bean的数据类型是用其它bean属性的数据类型，兼容并自动装配它。
     */
    private Object getByType(Map<String, XmlConfigBean> beans, String classPath, XmlConfigBean configFile) {
        //属性名称
        String proName = null;
        try {
            Class currentClass = Class.forName(classPath);
            Set<XmlBeanProperty> properties = configFile.getProperties();
            Constructor constructors[] = currentClass.getDeclaredConstructors();
//            当前对象
            Object currentObject = null;
            //当前属性对象,通过构造器创建对象
            Object currentProObj = null;
//            存储构造器的对象
            Map<String, Object> map = new HashMap<>();
            for (Constructor constructor : constructors) {
                Parameter parameters[] = constructor.getParameters();
                if (ArrayUtil.isNotEmpty(parameters) && parameters.length == 1) {
                    Class<?> type = parameters[0].getType();
                    String simpleName = ClassHelper.classNameToProName(type.getSimpleName());
                    Class proClass = Class.forName(beans.get(simpleName).getClazz());
                    currentProObj = proClass.newInstance();
                    currentObject = currentClass.getConstructor(type).newInstance(currentProObj);
                    map.put(simpleName, currentProObj);
                    break;
                } else if (ArrayUtil.isEmpty(parameters)) {
                    return currentClass.newInstance();
                }
            }
            //如果没有属性，就直接返回这个对象
            if (CollectionUtil.isEmpty(properties)) {
                return currentObject;
            }
            Field[] fields = currentClass.getDeclaredFields();
            for (XmlBeanProperty property : properties) {
                String name = property.getName();
                String ref = property.getRef();
                Object value = property.getValue();
                if (ArrayUtil.isNotEmpty(fields)) {
                    for (Field field : fields) {
                        proName = field.getName();
                        //ref对应的bean中的类型和当前属性的类型是否一致，或者是其实现类
                        if (StringUtils.isNotBlank(name) && name.equals(proName)) {
                            field.setAccessible(true);
                            String typeName = field.getType().getTypeName();
                            if (StringUtils.isBlank(ref) && value != null) {
                                Transfomer.transformerPropertyValue(currentObject, field, value);
                                break;
                            }
                            if (StringUtils.isNotBlank(ref) && value == null) {
                                XmlConfigBean configBean = beans.get(ref);
                                Class clazz = Class.forName(configBean.getClazz());
                                Class superClass = clazz.getSuperclass();
//                                如果有父类，而且当前bean中的类型和父类的类型相同
                                boolean hasSuperClass = superClass != null && !superClass.getSimpleName().equals("Object") && superClass.getTypeName().equals(typeName);
                                value = getBean(ref);
                                field.setAccessible(true);
                                if (hasSuperClass) {
                                    if (map.containsKey(name)) {
                                        value = currentProObj;
                                    }
                                    field.set(currentObject, value);
                                } else {
                                    Transfomer.transformerPropertyValue(field.getType().getSimpleName(), value);
                                }

                            }
                        }
                    }
                }
            }
            return currentObject;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("类" + classPath + "不存在");
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Created By zby on 2:27 2019/3/3
     * 如果找到默认的构造函数，使用“按构造器实现自动装配”;
     * 否则，使用“按类型自动装配”。
     */
    private Object getByAutodetect(Map<String, XmlConfigBean> beans, String classPath, XmlConfigBean configFile) {
        try {
            Class currentClass = Class.forName(classPath);
            Constructor constructors[] = currentClass.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                // 如果找到默认的构造器，按构造起创造对象
                Parameter[] parameters = constructor.getParameters();
                if (ArrayUtil.isEmpty(parameters)) {
                    return getBeanByConstruct(classPath, configFile);
                } else {
                    return getByType(beans, classPath, configFile);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


}
